//
//  PhotoViewerViewController.swift
//  Photomania
//
//  Created by Essan Parto on 2014-08-24.
//  Copyright (c) 2014 Essan Parto. All rights reserved.
//

import UIKit
import QuartzCore
import Alamofire

class PhotoViewerViewController: UIViewController, UIScrollViewDelegate, UIPopoverPresentationControllerDelegate, UIActionSheetDelegate {
  var photoID: Int = 0
  
  let scrollView = UIScrollView()
  let imageView = UIImageView()
  let spinner = UIActivityIndicatorView(activityIndicatorStyle: .WhiteLarge)
  
  var photoInfo: PhotoInfo?
  
  // MARK: Life-Cycle
  
  override func viewDidLoad() {
    super.viewDidLoad()
    
    setupView()
    loadPhoto()
  }
  
  /*
  这时，我们在其他 Alamofire 请求的完成处理方法中发出了 Alamofire 请求。第一个请求接收到了一个 JSON 响应数据，然后它使用我们新建的通用响应序列化方法，在JSON 数据之外创建了一个PhotoInfo实例。
  
  (_, _, photoInfo: PhotoInfo?, error) in是响应序列化方法的参数。刚开始的两个下划线"_"意味着我们忽略了前两个参数，我们无需明确地将它们明确命名为request和response。
  
  第三个参数明确被声明为了我们的PhotoInfo实例，因此通用序列化方法将自行初始化，并返回该类型的一个对象，其包含有图片 URL。第二个 Alamofire 请求使用您先前创建过的图片序列化方法，将NSData转化为UIImage，以便之后我们在图片视图中显示它。
  */
  func loadPhoto() {
    Alamofire.request(Five100px.Router.PhotoInfo(self.photoID, .Large)).validate().responseObject() {
      (_, _, photoInfo: PhotoInfo?, error) in
      
      if error == nil {
        self.photoInfo = photoInfo
        
        dispatch_async(dispatch_get_main_queue()) {
          self.addButtomBar()
          self.title = photoInfo!.name
        }
        
        
        /*
        在请求响应对象之前调用的.validate()函数是另一个易用的 Alamofire 特性。将其与请求和响应链接，以确认响应的状态码在默认可接受的范围（200到299）内。如果认证失败，响应处理方法将出现一个相关错误，您可以在完成处理方法中处理这个错误。
        
        即使没有发生错误，完成处理方法仍然还是会被调用。第四个参数error是NSError的一个实例，它的值让我们能够在自定义方法中响应这个错误。
        */
        Alamofire.request(.GET, photoInfo!.url).validate().responseImage() {
          (_, _, image, error) in
          
          if error == nil && image != nil {
            self.imageView.image = image
            self.imageView.frame = self.centerFrameFromImage(image)
            
            self.spinner.stopAnimating()
            
            self.centerScrollViewContents()
          }
        }
      }
    }
  }
  
  func setupView() {
    spinner.center = CGPoint(x: view.center.x, y: view.center.y - view.bounds.origin.y / 2.0)
    spinner.hidesWhenStopped = true
    spinner.startAnimating()
    view.addSubview(spinner)
    
    scrollView.frame = view.bounds
    scrollView.delegate = self
    scrollView.minimumZoomScale = 1.0
    scrollView.maximumZoomScale = 3.0
    scrollView.zoomScale = 1.0
    view.addSubview(scrollView)
    
    imageView.contentMode = .ScaleAspectFill
    scrollView.addSubview(imageView)
    
    let doubleTapRecognizer = UITapGestureRecognizer(target: self, action: "handleDoubleTap:")
    doubleTapRecognizer.numberOfTapsRequired = 2
    doubleTapRecognizer.numberOfTouchesRequired = 1
    scrollView.addGestureRecognizer(doubleTapRecognizer)
  }
  
  override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    
    if photoInfo != nil {
      navigationController?.setToolbarHidden(false, animated: true)
    }
  }
  
  override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    navigationController?.setToolbarHidden(true, animated: true)
  }
  
  // MARK: Bottom Bar
  
  func addButtomBar() {
    var items = [UIBarButtonItem]()
    
    let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .FlexibleSpace, target: nil, action: nil)
    
    items.append(barButtonItemWithImageNamed("hamburger", title: nil, action: "showDetails"))
    
    if photoInfo?.commentsCount > 0 {
      items.append(barButtonItemWithImageNamed("bubble", title: "\(photoInfo?.commentsCount ?? 0)", action: "showComments"))
    }
    
    items.append(flexibleSpace)
    items.append(UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Action, target: self, action: "showActions"))
    items.append(flexibleSpace)
    
    items.append(barButtonItemWithImageNamed("like", title: "\(photoInfo?.votesCount ?? 0)"))
    items.append(barButtonItemWithImageNamed("heart", title: "\(photoInfo?.favoritesCount ?? 0)"))
    
    self.setToolbarItems(items, animated: true)
    navigationController?.setToolbarHidden(false, animated: true)
  }
  
  func showDetails() {
    let photoDetailsViewController = storyboard?.instantiateViewControllerWithIdentifier("PhotoDetails") as? PhotoDetailsViewController
    photoDetailsViewController?.modalPresentationStyle = .OverCurrentContext
    photoDetailsViewController?.modalTransitionStyle = .CoverVertical
    photoDetailsViewController?.photoInfo = photoInfo
    
    presentViewController(photoDetailsViewController!, animated: true, completion: nil)
  }
  
  func showComments() {
    let photoCommentsViewController = storyboard?.instantiateViewControllerWithIdentifier("PhotoComments") as? PhotoCommentsViewController
    photoCommentsViewController?.modalPresentationStyle = .Popover
    photoCommentsViewController?.modalTransitionStyle = .CoverVertical
    photoCommentsViewController?.photoID = photoID
    photoCommentsViewController?.popoverPresentationController?.delegate = self
    presentViewController(photoCommentsViewController!, animated: true, completion: nil)
  }
  
  func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
    return UIModalPresentationStyle.OverCurrentContext
  }
  
  func presentationController(controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
    let navController = UINavigationController(rootViewController: controller.presentedViewController)
    
    return navController
  }
  
  func barButtonItemWithImageNamed(imageName: String?, title: String?, action: Selector? = nil) -> UIBarButtonItem {
    let button = UIButton.buttonWithType(.Custom) as! UIButton
    
    if imageName != nil {
      button.setImage(UIImage(named: imageName!)!.imageWithRenderingMode(.AlwaysTemplate), forState: .Normal)
    }
    
    if title != nil {
      button.setTitle(title, forState: .Normal)
      button.titleEdgeInsets = UIEdgeInsets(top: 0.0, left: 10.0, bottom: 0.0, right: 0.0)
      
      let font = UIFont.preferredFontForTextStyle(UIFontTextStyleFootnote)
      button.titleLabel?.font = font
    }
    
    let size = button.sizeThatFits(CGSize(width: 90.0, height: 30.0))
    button.frame.size = CGSize(width: min(size.width + 10.0, 60), height: size.height)
    
    if action != nil {
      button.addTarget(self, action: action!, forControlEvents: .TouchUpInside)
    }
    
    let barButton = UIBarButtonItem(customView: button)
    
    return barButton
  }
  
  // MARK: Download Photo
  
  func downloadPhoto() {
    /*
    
    1.我们首先请求一个新的PhotoInfo，此时请求的是XLarge大小。
    2.获取要保存文件的默认存储地址，我们将会将其存放在您应用的 Documents 目录的一个子目录中。该子目录的名字将和服务器建议的名字相同。destination是一个变相的闭包——虽然只是短短的一瞬间。
    3.Alamofire.download(_:_:_)方法和Alamofire.request(_:_)方法有很大的不同。Alamofire.download(_:_:_)方法不需要响应处理方法，也不需要响应序列化方法来对数据进行处理。因为它已经知道如何去处理这些数据了，就是把它们保存在硬盘上！destination闭包将返回要保存图片的路径。

    “为什么不直接使用同一个文件路径来保存呢？”。原因是在我们下载图片之前我们并不知道图片的名字。对于 500px.com 来说，服务器始终只会根据图片的尺寸返回1.jpg、2.jpg、3.jpg、4.jpg或者5.jpg这样的名字。我们不能够将相同名字的图片保存在同样的文件夹内。
    
    使用闭包作为Alamofire.download的第三个参数，而不是传递进一个固定的字符串路径。Alamofire 接着就会在一个合适的时间调用该闭包，然后将temporaryURL和NSHTTPURLResponse传递进来作为参数，然后返回一个 URL 的实例，指向硬盘上您有权访问的路径。
    */
    // 1
    Alamofire.request(Five100px.Router.PhotoInfo(photoInfo!.id, .XLarge)).validate().responseJSON() {
      (_, _, JSON, error) in
      
      if error == nil {
        let jsonDictionary = (JSON as! NSDictionary)
        let imageURL = jsonDictionary.valueForKeyPath("photo.image_url") as! String
        
        // 2
        //let destination = Alamofire.Request.suggestedDownloadDestination(directory: .DocumentDirectory, domain: .UserDomainMask)
        /*
        我们使用在闭包外捕获的图片id，然后将其和服务器建议的名称相连接，这两者之间使用“.”来分隔。
        */
        let destination: (NSURL, NSHTTPURLResponse) -> (NSURL) = {
          (temporaryURL, response) in
          
          if let directoryURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0] as? NSURL {
            return directoryURL.URLByAppendingPathComponent("\(self.photoInfo!.id).\(response.suggestedFilename)")
          }
          
          return temporaryURL
        }
        
        // 3
        //Alamofire.download(.GET, imageURL, destination)
        
        /*
        
        4.我们使用标准UIProgressView控件来显示图片下载进度。对其进行配置并将其添加到view视图上。
        5.借助 Alamofire，我们可以将.progress()和三个参数进行链接。.progress将定期调用一个闭包，而且这三个参数分别是：bytesRead、totalBytesRead以及totalBytesExpectedToRead。
        6.简单用totalBytesExpectedToRead除以totalBytesRead，我们就可以得到一个0到1之间的数字，这个数字代表这下载进度。如果下载时间不是瞬时的话，那么这个闭包可能会多次运行。每次运行我们都能够更新屏幕上的进度条。
        7.一旦下载结束，我们只需从view 视图上移去进度条。

        */
        // 4
        let progressIndicatorView = UIProgressView(frame: CGRect(x: 0.0, y: 80.0, width: self.view.bounds.width, height: 10.0))
        progressIndicatorView.tintColor = UIColor.blueColor()
        self.view.addSubview(progressIndicatorView)
        
        // 5
        Alamofire.download(.GET, imageURL, destination).progress {
          (_, totalBytesRead, totalBytesExpectedToRead) in
          
          dispatch_async(dispatch_get_main_queue()) {
            // 6
            progressIndicatorView.setProgress(Float(totalBytesRead) / Float(totalBytesExpectedToRead), animated: true)
            
            // 7
            if totalBytesRead == totalBytesExpectedToRead {
              progressIndicatorView.removeFromSuperview()
            }
          }
        }
      }
    }
  }

  func showActions() {
    let actionSheet = UIActionSheet(title: nil, delegate: self, cancelButtonTitle: "Cancel", destructiveButtonTitle: nil, otherButtonTitles: "Download Photo")
    actionSheet.showFromToolbar(navigationController?.toolbar)
  }
  
  func actionSheet(actionSheet: UIActionSheet, clickedButtonAtIndex buttonIndex: Int) {
    if buttonIndex == 1 {
      downloadPhoto()
    }
  }
  
  // MARK: Gesture Recognizers
  
  func handleDoubleTap(recognizer: UITapGestureRecognizer!) {
    let pointInView = recognizer.locationInView(self.imageView)
    self.zoomInZoomOut(pointInView)
  }
  
  // MARK: ScrollView
  
  func centerFrameFromImage(image: UIImage?) -> CGRect {
    if image == nil {
      return CGRectZero
    }
    
    let scaleFactor = scrollView.frame.size.width / image!.size.width
    let newHeight = image!.size.height * scaleFactor
    
    var newImageSize = CGSize(width: scrollView.frame.size.width, height: newHeight)
    
    newImageSize.height = min(scrollView.frame.size.height, newImageSize.height)
    
    let centerFrame = CGRect(x: 0.0, y: scrollView.frame.size.height/2 - newImageSize.height/2, width: newImageSize.width, height: newImageSize.height)
    
    return centerFrame
  }
  
  func scrollViewDidZoom(scrollView: UIScrollView) {
    self.centerScrollViewContents()
  }
  
  func centerScrollViewContents() {
    let boundsSize = scrollView.frame
    var contentsFrame = self.imageView.frame
    
    if contentsFrame.size.width < boundsSize.width {
      contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0
    } else {
      contentsFrame.origin.x = 0.0
    }
    
    if contentsFrame.size.height < boundsSize.height {
      contentsFrame.origin.y = (boundsSize.height - scrollView.scrollIndicatorInsets.top - scrollView.scrollIndicatorInsets.bottom - contentsFrame.size.height) / 2.0
    } else {
      contentsFrame.origin.y = 0.0
    }
    
    self.imageView.frame = contentsFrame
  }
  
  func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
    return self.imageView
  }
  
  func zoomInZoomOut(point: CGPoint!) {
    let newZoomScale = self.scrollView.zoomScale > (self.scrollView.maximumZoomScale/2) ? self.scrollView.minimumZoomScale : self.scrollView.maximumZoomScale
    
    let scrollViewSize = self.scrollView.bounds.size
    
    let width = scrollViewSize.width / newZoomScale
    let height = scrollViewSize.height / newZoomScale
    let x = point.x - (width / 2.0)
    let y = point.y - (height / 2.0)
    
    let rectToZoom = CGRect(x: x, y: y, width: width, height: height)
    
    self.scrollView.zoomToRect(rectToZoom, animated: true)
  }
}
